<?php
/**
 * @author spenceryg
 * @date 2022/11/29
 * @time 9:27 下午
 * @focus love & life
 */


namespace app\auto\business;


use app\admin\controller\auto\service\TableRelationService;
use app\auto\lib\ModelGenerator;
use app\auto\model\Relation;
use app\auto\model\Table;
use app\auto\validate\RelationValidate;
use app\common\lib\mysql\TableModify;
use app\common\lib\mysql\TpTableModify;
use Laminas\Code\Generator\ValueGenerator;
use think\helper\Str;

class RelationBusiness
{
    public static $types = [
        'hasOne' => '有一个',
        'hasMany' => '有多个',
        'hasOneThrough' => '有一个（远程）',
        'hasManyThrough' => '有多个（远程）',
        'belongsToMany' => '多对多',
        'morphMany' => '有多态个'
    ];


    public static $type_relations = [
        'hasOne' => 'belongsTo',
        'hasMany' => 'belongsTo',
        'hasOneThrough' => '',
        'hasManyThrough' => '',
        'belongsToMany' => 'belongsToMany',
        'morphMany' => ''
    ];

    public static function add($data)
    {
        validate(RelationValidate::class)->check($data);
        switch ($data['type']) {
            case 'belongsToMany':
                $master = Table::find($data['table_id_master']);
                $sliver = Table::find($data['table_id_sliver']);
                $master_field = str_replace(env('DATABASE.PREFIX'), '', $master['name']) . '_id';
                $sliver_field = str_replace(env('DATABASE.PREFIX'), '', $sliver['name']) . '_id';
                $table_name = $master['name'] . '_' . str_replace(env('DATABASE.PREFIX'), '', $sliver['name']);
                // 生成中间表
                $table_generator = (new TpTableModify($table_name));
                $table_generator->createTable($master['comment'] . "[中间表]" . $sliver['comment'])->execute();
                $table_generator->add($master_field, 'int', null, 11, $master['name'] . 'id')->execute();
                $table_generator->add($sliver_field, 'int', null, 11, $sliver['name'] . 'id')->execute();
                break;
            case 'hasOne':
            case 'hasMany':
                $master = Table::find($data['table_id_master']);
                $sliver = Table::find($data['table_id_sliver']);
                $add_filed_table_name = $sliver['name'];
                $add_field_name = Str::snake(str_replace(env('DATABASE.PREFIX'), '', $master['name'])) . '_id';
                $relation_filed_exist = Relation::where('table_id_sliver', $data['table_id_sliver'])->where('sliver_field', $add_field_name)->find();
                $middle_filed_exits = Relation::where('table_id_middle', $data['table_id_sliver'])->where('middle_field', $add_field_name)->find();
                if (!$relation_filed_exist && !$middle_filed_exits) {
                    self::createFieldInt($add_filed_table_name, $add_field_name, $master['comment'] . '关联id');
                }
                $data['sliver_field'] = $add_field_name;
                break;
            case 'hasOneThrough':
            case 'hasManyThrough':
                $master = Table::find($data['table_id_master']);
                $sliver = Table::find($data['table_id_sliver']);
                $middle = Table::find($data['table_id_middle']);

                // sliver_filed
                $add_filed_table_name = $sliver['name'];
                $add_field_name = Str::snake(str_replace(env('DATABASE.PREFIX'), '', $middle['name'])) . '_id';
                $relation_filed_exist = Relation::where('table_id_sliver', $data['table_id_sliver'])->where('sliver_field', $add_field_name)->find();
                $middle_filed_exits = Relation::where('table_id_middle', $data['table_id_sliver'])->where('middle_field', $add_field_name)->find();
                if (!$relation_filed_exist && !$middle_filed_exits) {
                    self::createFieldInt($add_filed_table_name, $add_field_name, $master['comment'] . '关联id');
                }
                $data['sliver_field'] = $add_field_name;

                // middle_field
                $add_filed_table_name = $middle['name'];
                $add_field_name = Str::snake(str_replace(env('DATABASE.PREFIX'), '', $master['name'])) . '_id';
                $relation_filed_exist = Relation::where('table_id_sliver', $data['table_id_middle'])->where('sliver_field', $add_field_name)->find();
                $middle_filed_exits = Relation::where('table_id_middle', $data['table_id_middle'])->where('middle_field', $add_field_name)->find();
                if (!$relation_filed_exist && !$middle_filed_exits) {
                    self::createFieldInt($add_filed_table_name, $add_field_name, $master['comment'] . '关联id');
                }
                $data['middle_field'] = $add_field_name;
                break;
            case 'morphMany':
                // todo
                break;
            default:
                break;
        }


        Relation::create($data);
    }

    public static function delete($id)
    {
        $data = Relation::find($id);
        $type = $data['type'];
        switch ($type) {
            case 'belongsToMany':
                $master = Table::find($data['table_id_master']);
                $sliver = Table::find($data['table_id_sliver']);
                $table_name = $master['name'] . '_' . str_replace(env('DATABASE.PREFIX'), '', $sliver['name']);
                // 删除中间表
                TableBusiness::dropTable($table_name);
                // 删除中间表模型文件
                TableBusiness::deleteModelFile($table_name);
                break;
            case 'hasOneThrough':
            case 'hasManyThrough':
                $master = Table::find($data['table_id_master']);
                $sliver = Table::find($data['table_id_sliver']);
                $middle = Table::find($data['table_id_middle']);

                // sliver_filed 删除
                $add_filed_table_name = $sliver['name'];
                $add_field_name = Str::snake(str_replace(env('DATABASE.PREFIX'), '', $middle['name'])) . '_id';
                $relation_filed_count = Relation::where('table_id_sliver', $data['table_id_sliver'])->where('sliver_field', $add_field_name)->count();
                $middle_filed_count = Relation::where('table_id_middle', $data['table_id_sliver'])->where('middle_field', $add_field_name)->count();
                if ($relation_filed_count + $middle_filed_count == 1) {
                    TableBusiness::deleteField($add_filed_table_name, $add_field_name);
                }

                // middle_field 删除
                $add_filed_table_name = $middle['name'];
                $add_field_name = Str::snake(str_replace(env('DATABASE.PREFIX'), '', $master['name'])) . '_id';
                $relation_filed_count = Relation::where('table_id_sliver', $data['table_id_sliver'])->where('sliver_field', $add_field_name)->count();
                $middle_filed_count = Relation::where('table_id_middle', $data['table_id_sliver'])->where('middle_field', $add_field_name)->count();
                if ($relation_filed_count + $middle_filed_count == 1) {
                    TableBusiness::deleteField($add_filed_table_name, $add_field_name);
                }
                break;
            case 'hasOne':
            case 'hasMany':
                $master = Table::find($data['table_id_master']);
                $sliver = Table::find($data['table_id_sliver']);
                $add_filed_table_name = $sliver['name'];
                $add_field_name = Str::snake(str_replace(env('DATABASE.PREFIX'), '', $master['name'])) . '_id';
                $relation_filed_count = Relation::where('table_id_sliver', $data['table_id_sliver'])->where('sliver_field', $add_field_name)->count();
                $middle_filed_count = Relation::where('table_id_middle', $data['table_id_sliver'])->where('middle_field', $add_field_name)->count();
                if ($relation_filed_count + $middle_filed_count == 1) {
                    TableBusiness::deleteField($add_filed_table_name, $add_field_name);
                }
                $data['sliver_field'] = $add_field_name;
                break;
            default:
                break;
        }
        Relation::destroy($id);
    }


    public static function createFieldInt($table_name, $name, $comment, $default = null)
    {
        FieldBusiness::add([
            'name' => $name,
            'comment' => $comment,
            'type' => 'int',
            'length' => '11',
            'default' => $default,
            'is_null' => 1,
            'table_id' => Table::getByName($table_name)['id'],
            'is_relation' => 1,
            'sort' => 0
        ]);
    }


    /**
     * 根据表名获取模型的的最大化关联
     * getModelFullRelation
     * @param $table_name
     * @return array
     */
    public static function getModelFullRelation($table_name): array
    {
        $res = [];
        $data = Relation::select();
        foreach ($data as $item) {
            $master_table = Table::find($item['table_id_master']);
            $sliver_table = Table::find($item['table_id_sliver']);
            $this_item['id'] = $item['id'];
            $this_item['master_table'] = $master_table['name'];
            $this_item['sliver_table'] = $sliver_table['name'];
            $this_item['relation'] = $item['type'];
            $this_item['master_name'] = TableBusiness::tableToRelation($sliver_table['name']);
            $this_item['sliver_name'] = TableBusiness::tableToRelation($master_table['name']);
            $this_item['field'] = $item['sliver_field'];
            $this_item['table_id_master'] = $item['table_id_master'];
            $this_item['table_id_sliver'] = $item['table_id_sliver'];
            $res[] = $this_item;
        }
        $res1 = $res;
        $data = [];
        $full = self::getFullRelation($res, $table_name, $data);

        $result['full_str'] = (new ValueGenerator($full))->generate();
        $result['data_str'] = (new ValueGenerator($data))->generate();
        $result['full'] = $full;
        $result['data'] = $data;

        $data = [];
        $full_id = self::getFullRelationId($res1, $table_name, $data);
        $result['full_id'] = $full_id;
        return $result;
    }


    /**
     * 一维数组转换为关联多维 relation
     * getFullRelation
     * @param $arr
     * @param $table_name
     * @param $data
     * @param int $pid
     * @return array
     */
    private static function getFullRelation(&$arr, $table_name, &$data, int $pid = 0): array
    {
        $full = [];
        foreach ($arr as $key => $item) {
            if ($item['master_table'] == $table_name) {
                if ($pid) {
                    $item['pid'] = $pid;
                    $pids = $data[$pid]['pids'];
                    $pids[] = $pid;
                    $item['pids'] = $pids;
                } else {
                    $item['pid'] = '';
                    $item['pids'] = [];
                }

                $item['link_table'] = $item['sliver_table'];
                $item['local_table'] = $item['master_table'];
                if ($item['relation'] == 'belongsToMany') {
                    $item['link_field'] = str_replace(env('DATABASE.PREFIX'), '', $item['link_table']) . '_id';
                    $item['local_field'] = str_replace(env('DATABASE.PREFIX'), '', $item['local_table']) . '_id';
                } else {
                    $item['link_field'] = $item['field'];
                    $item['local_field'] = 'id';
                }


                $data[$item['id']] = $item;
                unset($arr[$key]);
                $full[$item['master_name']] = self::getFullRelation($arr, $item['sliver_table'], $data, $item['id']);
            }
            if ($item['sliver_table'] == $table_name) {
                if ($pid) {
                    $item['pid'] = $pid;
                    $pids = $data[$pid]['pids'];
                    $pids[] = $pid;
                    $item['pids'] = $pids;
                } else {
                    $item['pid'] = '';
                    $item['pids'] = [];
                }

                $item['link_table'] = $item['master_table'];
                $item['local_table'] = $item['sliver_table'];
                if ($item['relation'] == 'belongsToMany') {
                    $item['link_field'] = str_replace(env('DATABASE.PREFIX'), '', $item['link_table']) . '_id';
                    $item['local_field'] = str_replace(env('DATABASE.PREFIX'), '', $item['local_table']) . '_id';
                } else {
                    $item['link_field'] = 'id';
                    $item['local_field'] = $item['field'];
                }


                $data[$item['id']] = $item;
                unset($arr[$key]);
                $full[$item['sliver_name']] = self::getFullRelation($arr, $item['master_table'], $data, $item['id']);
            }
        }
        return $full;
    }


    /**
     * 一维数组转换为关联多维 relation
     * getFullRelation
     * @param $arr
     * @param $table_name
     * @param $data
     * @param int $pid
     * @return array
     */
    private static function getFullRelationId(&$arr, $table_name, &$data, int $pid = 0): array
    {
        $full = [];
        foreach ($arr as $key => $item) {
            if ($item['master_table'] == $table_name) {
                if ($pid) {
                    $item['pid'] = $pid;
                    $pids = $data[$pid]['pids'];
                    $pids[] = $pid;
                    $item['pids'] = $pids;
                } else {
                    $item['pid'] = '';
                    $item['pids'] = [];
                }

                $item['link_table'] = $item['sliver_table'];
                $item['local_table'] = $item['master_table'];
                $item['link_field'] = $item['field'];
                $item['local_field'] = 'id';


                $data[$item['id']] = $item;
                unset($arr[$key]);
                $full[$item['id'] . '_' . $item['table_id_sliver']] = self::getFullRelationId($arr, $item['sliver_table'], $data, $item['id']);
            }
            if ($item['sliver_table'] == $table_name) {
                if ($pid) {
                    $item['pid'] = $pid;
                    $pids = $data[$pid]['pids'];
                    $pids[] = $pid;
                    $item['pids'] = $pids;
                } else {
                    $item['pid'] = '';
                    $item['pids'] = [];
                }

                $item['link_table'] = $item['master_table'];
                $item['local_table'] = $item['sliver_table'];
                $item['link_field'] = 'id';
                $item['local_field'] = $item['field'];


                $data[$item['id']] = $item;
                unset($arr[$key]);
                $full[$item['id'] . '_' . $item['table_id_master']] = self::getFullRelationId($arr, $item['master_table'], $data, $item['id']);
            }
        }
        return $full;
    }


    /**
     * 获取最小链路
     * getDbMini
     * @param string $table_name
     * @param array $ids 相关表名数组
     * @return string
     */
    public static function getDbMini(string $table_name, array $ids = []): string
    {

        $self_id = Table::where('name', $table_name)->value('id');

        $ids = array_filter($ids, function ($value) use ($self_id) {
            return $value != $self_id;
        });

        $res = "\$query = Db::table('{$table_name}');\n";
        $data = self::getModelFullRelation($table_name);
        $full = $data['full_id'];
        $data = $data['data'];

        $maps = self::getDbMap($full);
        $id_arr = [];
        foreach ($maps as $key => $map) {
            if (in_array($key, $ids)) {
                foreach ($map as $id) {
                    $id_arr[] = $id;
                }
            }
        }
        $id_arr = array_unique($id_arr);


        $left_table = [];
        foreach ($data as $item) {
            if (in_array($item['id'], $id_arr)) {
                switch ($item['relation']) {
                    case 'hasOneThrough':
                    case 'hasManyThrough':
                    case 'morphMany':
                        break;
                    case 'belongsToMany':
                        $mid_table = $item['master_table'] . str_replace(env('DATABASE.PREFIX'), '_', $item['sliver_table']);
                        $res .= "\$query->join('{$mid_table}', '{$mid_table}.{$item['local_field']} = {$item['local_table']}.id');\n";
                        $res .= "\$query->join('{$item['link_table']}', '{$item['link_table']}.id = {$mid_table}.{$item['link_field']}');\n";
                        break;
                    default:
                        if (in_array($item['link_table'], $left_table)) {
                            break;
                        } else {
                            $left_table[] = $item['link_table'];
                            $res .= "\$query->join('{$item['link_table']}', '{$item['link_table']}.{$item['link_field']} = {$item['local_table']}.{$item['local_field']}');\n";
                        }
                        break;
                }
            }
        }
        return $res;
    }


    private static function getDbMap($full, $res = [], &$result = []): array
    {
        foreach ($full as $key => $val) {
            $res1 = $res;
            [$relation_id, $table_id] = explode('_', $key);
            $res1[] = $relation_id;
            if (!isset($result[$table_id]) || sizeof($result[$table_id]) > sizeof($res1)) {
                $result[$table_id] = $res1;
            }
            if ($val) {
                array_merge($result, self::getDbMap($val, $res1, $result));
            }
        }
        return $result;
    }


    /**
     * 根据条件获取关联查询代码
     * getCode
     * @param $data
     * @return string
     */
    public static function getCode($data): string
    {
        $main_table_name = $data['table_name'];
        $ids = [];
        $where_html = '';

        foreach ($data['in_tables'] as $index => $table_name) {
            if (!$data['in_tables'][$index] || !$data['in_fields'][$index]) {
                continue;
            }
            $table = Table::getByName($table_name);
            $ids[] = $table['id'];
            [$condition, $value] = explode(' ', $data['in_conditions'][$index]);
            if (strpos($value, 'input') !== false) {
                $real_fields = str_replace(env('DATABASE.PREFIX'), '', $data['in_tables'][$index]) . '__' . $data['in_fields'][$index];
                $value = "input('$real_fields', '')";
                $value1 = "input('$real_fields', '', 'search_in')";


                switch ($condition) {
                    case 'like':
                        $where_html .= <<<EOF
if({$value} !== ''){
    \$query->where('{$data['in_tables'][$index]}.{$data['in_fields'][$index]}', '{$condition}', '%'. {$value1} .'%');
}

EOF;
                        break;
                    case '=':
                        $where_html .= <<<EOF
if({$value} !== ''){
    \$query->where('{$data['in_tables'][$index]}.{$data['in_fields'][$index]}', '{$condition}', {$value1});
}

EOF;
                        break;
                    case 'between_date':
                        $condition = 'between';
                        $where_html .= <<<EOF
if(input('{$real_fields}_start', '') || input('{$real_fields}_end', '')){
    \${$real_fields}_start = input('{$real_fields}_start', '', 'search_in');
    \${$real_fields}_end = input('{$real_fields}_end', '', 'search_in');
    \${$real_fields}_start = \${$real_fields}_start ? strtotime(\${$real_fields}_start) : 0;
    \${$real_fields}_end = \${$real_fields}_end ? strtotime(\${$real_fields}_end) + 24*60*60 - 1 : time();
    \$query->where('{$data['in_tables'][$index]}.{$data['in_fields'][$index]}', '{$condition}', [\${$real_fields}_start,\${$real_fields}_end]);
}

EOF;

                        break;
                }

            } else {
                $where_html .= <<<EOF
\$query->where('{$data['in_tables'][$index]}.{$data['in_fields'][$index]}', '{$condition}', '{$value}');

EOF;
            }

        }

        if ($data['out_tables']) {
            foreach ($data['out_tables'] as $index => $table_name) {
                if (!$table_name || !$data['out_fields'][$index]) {
                    continue;
                }
                $table = Table::getByName($table_name);
                $ids[] = $table['id'];
            }
        }


        $ids = array_unique($ids);
        $db_html = self::getDbMini($main_table_name, $ids);
        $db_html = trim($db_html);


        $where_delete_html = '';
        foreach ($ids as $id) {
            $table = Table::find($id);
            if ($table['is_soft_delete']) {
                $where_delete_html .= "\$query->where('{$table['name']}.delete_time','=', null);";
            }
        }

        $fields_html = '';
        if ($data['out_tables']) {
            foreach ($data['out_tables'] as $index => $table_name) {
                if (!$table_name || !$data['out_fields'][$index]) {
                    continue;
                }
                $fields_html .= <<<EOF
\$query->field(['{$data['out_tables'][$index]}.{$data['out_fields'][$index]}' => '{$data['out_alias'][$index]}']);
EOF;
                if(strstr($data['out_fields'][$index], 'time')){
                    $fields_html .= <<<EOF
\$query->withAttr('{$data['out_alias'][$index]}' , function(\$value, \$data){return date('Y-m-d H:i:s', \$value);});
EOF;
                }elseif(strstr($data['out_fields'][$index], 'date')){
                    $fields_html .= <<<EOF
\$query->withAttr('{$data['out_alias'][$index]}' , function(\$value, \$data){return date('Y-m-d, \$value);});
EOF;
                }
            }
        }

        $order_html = <<<EOF
\$query->order('{$main_table_name}.id', 'desc');
EOF;

        return <<<EOF
$db_html
$where_html
$where_delete_html
$fields_html
$order_html
EOF;
    }

}
